package com.zhdl.network.prefiretcp;


import com.project.common.util.CRC16;
import com.zhdl.common.ytools.ByteHelper;
import com.zhdl.modules.prefiresys.web.request.SetDetectorParamRequest;
import com.zhdl.modules.prefiresys.web.request.SetFactoryParamRequest;
import com.zhdl.network.prefiretcp.before.CmdTypeReq;
import com.zhdl.network.service.dto.SingleParaInfo;
import com.zhdl.network.service.dto.SysHelper;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;


/**
 * 组装发送数据包
 */
public class FramePkg {
    public static final byte SYS_ID = 3;
    /**
     * 是否加密帧
     */
    private boolean _encrypt;
    /**
     * 设备 ID
     */
    private short _detectorAddr;
    /**
     * 数据包序列号 0~255 每个传输部件都会对发送序列进行计数，从 0 至 255，到 255 后清零重新开始计数。该结构能够检测到数据包的丢失
     */
    private short _seq = 0x00;

    /**
     * @param encrypt 是否加密帧
     */
    public FramePkg(short detectorAddr, boolean encrypt) {
        this._detectorAddr = detectorAddr;
        this._encrypt = encrypt;
    }

    public void SetEncrypt(boolean value) {
        this._encrypt = value;
    }

    /**
     * 数据包序列号
     */
    private byte GetSeq() {
        if (SysHelper.debug) {
            // 测试时为联调方便，序号不再累加（命令不变可以自动响应），对接设备试运行时可以取消注释
            return (byte) 0x01;
        } else {
            _seq++;
            if (_seq > 255) return 0;
            if (_seq < 0) return 0;
            return ByteHelper.UByte(_seq);
        }
    }

    /**
     * 组装发送数据包
     *
     * @param cmdByte  命令类型
     * @param unitData 单元数据
     * @return 16进制字符串
     */
    public byte[] buildMainPkg(byte cmdByte, byte[] unitData) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream(unitData.length + 8);
        // 数据包开始标志 非加密帧 0x55，加密帧 0xA5
        bw.write(this._encrypt ? (byte) 0xA5 : (byte) 0x55);

        // 有效载荷长度
        bw.write(unitData.length);

        // 数据包序列号
        bw.write(GetSeq());

        // 系统 ID
        bw.write(SYS_ID);

        // 设备地址 1-255
        bw.write(this._detectorAddr);

        // 命令
        bw.write(cmdByte);

        // 数据 data
        bw.write(unitData);

        // 校验和 2HEX 从消息包的 1~（n+5）字节（不包括 STX），除去开头STX，除去结尾的校验和
        byte[] bsTemp = bw.toByteArray();
        byte[] calcBytes = new byte[bsTemp.length - 1];
        System.arraycopy(bsTemp, 1, calcBytes, 0, calcBytes.length);
        byte[] crc = CRC16.XMODEM_LH(calcBytes);
        bw.write(crc);

        return bw.toByteArray();
    }

    /**
     * 查询参数
     *
     * @param id id
     * @return byte[]
     * @throws IOException ex
     */
    public byte[] GetPara(byte[] id) throws IOException {
        return buildMainPkg(CmdTypeReq.GetPara.getValue(), id);
    }

    /**
     * 设置参数
     *
     * @param paraList paraList
     * @return byte[]
     * @throws IOException ex
     */
    public byte[] SetPara(List<SingleParaInfo> paraList) throws IOException {
        ArrayList<Byte> byteArray = new ArrayList<>();
        for (SingleParaInfo paraInfo : paraList) {
            // 序号
            byteArray.add(paraInfo.getId());
            // 长度
            byteArray.add(ByteHelper.UByte(paraInfo.getLength()));
            // 数据
            for (byte b : paraInfo.getData())
                byteArray.add(b);
        }
        byte[] unitBuff = ByteHelper.arrayListByteToArray(byteArray);

        return buildMainPkg(CmdTypeReq.SetPara.getValue(), unitBuff);
    }

    /**
     * 设置探测器设备参数
     *
     * @return byte[]
     * @throws IOException ex
     */
    public byte[] setDetectorPara(SetDetectorParamRequest body) throws IOException {
        List<SingleParaInfo> paraList = new ArrayList<>();
        paraList.add(new SingleParaInfo(0x13, body.getOutputRelayCtrl1()));
        paraList.add(new SingleParaInfo(0x14, body.getOutputRelayCtrl2()));
        paraList.add(new SingleParaInfo(0x15, body.getOutputRelayCtrl3()));
        paraList.add(new SingleParaInfo(0x16, body.getOutputRelayCtrl4()));

        paraList.add(new SingleParaInfo(0xA0, body.getSensitivity()));
        paraList.add(new SingleParaInfo(0xA1, body.getFareAlarmThresholdValue()));
        paraList.add(new SingleParaInfo(0xA2, body.getFareAlarmThresholdValue3()));
        paraList.add(new SingleParaInfo(0xA3, body.getFareAlarmThresholdValue2()));
        paraList.add(new SingleParaInfo(0xA4, body.getFareAlarmThresholdValue1()));
        paraList.add(new SingleParaInfo(0xA5, body.getSampling1HighLimit()));
        paraList.add(new SingleParaInfo(0xA6, body.getSampling2HighLimit()));
        paraList.add(new SingleParaInfo(0xA7, body.getSampling3HighLimit()));
        paraList.add(new SingleParaInfo(0xA8, body.getSampling4HighLimit()));
        paraList.add(new SingleParaInfo(0xA9, body.getFanSpeed()));
        paraList.add(new SingleParaInfo(0xAC, body.getInput1()));
        paraList.add(new SingleParaInfo(0xAD, body.getInput2()));
        paraList.add(new SingleParaInfo(0xAE, body.getInput3()));
        paraList.add(new SingleParaInfo(0xAF, body.getInput4()));
        String bitStr = String.format("%s%s%s%s",
                body.getInput4Status(),
                body.getInput3Status(),
                body.getInput2Status(),
                body.getInput1Status());
        paraList.add(new SingleParaInfo(0xB0, ByteHelper.bitString2Byte(bitStr)));
        paraList.add(new SingleParaInfo(0xB1, body.getOutput1()));
        paraList.add(new SingleParaInfo(0xB2, body.getOutput2()));
        paraList.add(new SingleParaInfo(0xB3, body.getOutput3()));
        paraList.add(new SingleParaInfo(0xB4, body.getOutput4()));
        paraList.add(new SingleParaInfo(0xB5, body.getOutput1Delay()));
        paraList.add(new SingleParaInfo(0xB6, body.getOutput2Delay()));
        paraList.add(new SingleParaInfo(0xB7, body.getOutput3Delay()));
        paraList.add(new SingleParaInfo(0xB8, body.getOutput4Delay()));
        bitStr = String.format("%s%s%s%s",
                body.getOutput4Status(),
                body.getOutput3Status(),
                body.getOutput2Status(),
                body.getOutput1Status());
        paraList.add(new SingleParaInfo(0xB9, ByteHelper.bitString2Byte(bitStr)));
        paraList.add(new SingleParaInfo(0xBA, body.getAlarmVolume()));
        paraList.add(new SingleParaInfo(0xBB, body.getBgConcentration()));
        paraList.add(new SingleParaInfo(0xBC, body.getSampling1LowLimit()));
        paraList.add(new SingleParaInfo(0xBD, body.getSampling2LowLimit()));
        paraList.add(new SingleParaInfo(0xBE, body.getSampling3LowLimit()));
        paraList.add(new SingleParaInfo(0xBF, body.getSampling4LowLimit()));

        return SetPara(paraList);
    }

    /**
     * 设置厂家参数
     *
     * @return byte[]
     * @throws IOException ex
     */
    public byte[] setFactoryPara(SetFactoryParamRequest body) throws IOException {
        List<SingleParaInfo> paraList = new ArrayList<>();
        paraList.add(new SingleParaInfo(0xE0, body.getAreaCount()));
        paraList.add(new SingleParaInfo(0xE1, body.getFlowRateCheckFactor1()));
        paraList.add(new SingleParaInfo(0xE2, body.getFlowRateCheckFactor2()));
        paraList.add(new SingleParaInfo(0xE3, body.getFlowRateCheckFactor3()));
        paraList.add(new SingleParaInfo(0xE4, body.getFlowRateCheckFactor4()));
        byte[] bsTemp = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putFloat(body.getPdDetectVoltageValue()).array();
        paraList.add(new SingleParaInfo(0xE5, bsTemp));
        paraList.add(new SingleParaInfo(0xE6, ByteHelper.u16ToByteArrayLow(body.getWaterLevelLimit())));
        paraList.add(new SingleParaInfo(0xE7, ByteHelper.u16ToByteArrayLow(body.getSmokeLimit())));
        paraList.add(new SingleParaInfo(0xE8, ByteHelper.u16ToByteArrayLow(body.getSignBaseValue())));
        paraList.add(new SingleParaInfo(0xE9, body.getProductModel()));
        String bitStr = String.format("%s%s%s%s",
                body.isFlowSpeedSensorEnable4() ? 1 : 0,
                body.isFlowSpeedSensorEnable3() ? 1 : 0,
                body.isFlowSpeedSensorEnable2() ? 1 : 0,
                body.isFlowSpeedSensorEnable1() ? 1 : 0);
        paraList.add(new SingleParaInfo(0xEA, ByteHelper.bitString2Byte(bitStr)));
        bitStr = String.format("%s%s",
                body.isSmokeModuleEnable() ? 1 : 0,
                body.isParticleModuleEnable() ? 1 : 0);
        paraList.add(new SingleParaInfo(0xEB, ByteHelper.bitString2Byte(bitStr)));

        return SetPara(paraList);
    }


    /**
     * 查询常规参数，批量返回常用参数，固定位置，不带长度，比通用的查询参数简单一些
     *
     * @return byte[]
     * @throws IOException ex
     */
    public byte[] GeneralParamMonitor() throws IOException {
        byte[] unitBuff = new byte[6];
        if (!SysHelper.debug) {
            Calendar calendar = Calendar.getInstance();
            unitBuff[0] = (byte) (calendar.get(Calendar.YEAR) - 2000);
            unitBuff[1] = (byte) (calendar.get(Calendar.MONTH) + 1);
            unitBuff[2] = (byte) calendar.get(Calendar.DATE);
            unitBuff[3] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
            unitBuff[4] = (byte) calendar.get(Calendar.MINUTE);
            unitBuff[5] = (byte) calendar.get(Calendar.SECOND);
        }
        return buildMainPkg(CmdTypeReq.GeneralParamMonitor.getValue(), unitBuff);
    }

    /**
     * 静音/取消静音
     *
     * @return byte[]
     * @throws IOException ex
     */
    public byte[] CtrlSound(boolean soundEnable) throws IOException {
        byte[] unitBuff = new byte[6];
        if (!SysHelper.debug) {
            Calendar calendar = Calendar.getInstance();
            unitBuff[0] = (byte) (calendar.get(Calendar.YEAR) - 2000);
            unitBuff[1] = (byte) (calendar.get(Calendar.MONTH) + 1);
            unitBuff[2] = (byte) calendar.get(Calendar.DATE);
            unitBuff[3] = (byte) calendar.get(Calendar.HOUR);
            unitBuff[4] = (byte) calendar.get(Calendar.MINUTE);
            unitBuff[5] = (byte) calendar.get(Calendar.SECOND);
        }
        return buildMainPkg(CmdTypeReq.SetPara.getValue(), unitBuff);
    }


}
